package com.simplemobiletools.camera.voucher;

import android.graphics.Bitmap;
import android.graphics.Point;
import android.os.Build;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Toast;

import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.view.ViewCompat;

import com.huawei.hmf.tasks.OnSuccessListener;
import com.huawei.hmf.tasks.Task;
import com.huawei.hms.mlsdk.common.MLFrame;
import com.huawei.hms.mlsdk.dsc.MLDocumentSkewCorrectionAnalyzer;
import com.huawei.hms.mlsdk.dsc.MLDocumentSkewCorrectionAnalyzerFactory;
import com.huawei.hms.mlsdk.dsc.MLDocumentSkewCorrectionAnalyzerSetting;
import com.huawei.hms.mlsdk.dsc.MLDocumentSkewDetectResult;
import com.simplemobiletools.camera.R;
import com.simplemobiletools.camera.document.view.CropImageView;
import com.simplemobiletools.camera.document.view.CropView;
import com.simplemobiletools.camera.gpuimage.Camera1Loader;
import com.simplemobiletools.camera.gpuimage.Camera2Loader;
import com.simplemobiletools.camera.gpuimage.CameraLoader;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import jp.co.cyberagent.android.gpuimage.GPUImageView;
import jp.co.cyberagent.android.gpuimage.util.Rotation;
import kotlin.Unit;
import kotlin.jvm.functions.Function1;

/**
 * All rights Reserved, Designed By www.rongdasoft.com
 *
 * @version V1.0
 * @Title: VoucherCameraActivity
 * @Description:
 * @author: wuyongzheng
 * @date: 2022/3/17
 * @Copyright: 2022/3/17 www.rongdasoft.com Inc. All rights reserved.
 */
public class VoucherCameraActivity extends AppCompatActivity {

    private CameraLoader cameraLoader;
    private GPUImageView gpuImageView;

    private CameraLoader loadCamera() {
        if (cameraLoader != null) {
            return cameraLoader;
        }
        if (Build.VERSION.SDK_INT < 21) {
            cameraLoader = new Camera1Loader(this);
        } else {
            cameraLoader = new Camera2Loader(this);
        }
        return cameraLoader;
    }

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_voucher_camera);
        // initView
        gpuImageView = findViewById(R.id.surfaceView);
        cropView = findViewById(R.id.vCrop);
        cropImageView = findViewById(R.id.ivCrop);
        nv21ToBitmap = new NV21ToBitmap(this);
        // initCallback
        findViewById(R.id.image_capture).setOnClickListener(v -> {
            String folderName = "GPUImage";
            String fileName = System.currentTimeMillis() + ".jpg";
            gpuImageView.saveToPictures(folderName, fileName,
                uri -> Toast.makeText(VoucherCameraActivity.this, uri.toString(), Toast.LENGTH_SHORT).show());
        });
        findViewById(R.id.img_switch_camera).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                CameraLoader cameraLoader = loadCamera();
                cameraLoader.switchCamera();
                gpuImageView.setRotation(getRotation(VoucherCameraActivity.this.cameraLoader.getCameraOrientation()));
            }
        });
        findViewById(R.id.img_auto_document).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                switchAutoDocument(v);
            }
        });
        // initData
        loadCamera().setOnPreviewFrameListener((data, width, height) -> {
            gpuImageView.updatePreviewFrame(data, width, height);
            if (isAutoDocument) {
                executorService.execute(new Runnable() {
                    @Override
                    public void run() {
                        if (!isAutoDocument) return;
                        if (nv21ToBitmap == null) return;
                        Bitmap bitmap = nv21ToBitmap.nv21ToBitmap(data, width, height);
                        MLFrame mlFrame = MLFrame.fromBitmap(bitmap);
                        Task<MLDocumentSkewDetectResult> tasks =
                            getMLDocAnalyzer().asyncDocumentSkewDetect(mlFrame);
                        tasks.addOnSuccessListener(new OnSuccessListener<MLDocumentSkewDetectResult>() {
                            @Override
                            public void onSuccess(MLDocumentSkewDetectResult mlDocumentSkewDetectResult) {
                                if (mlDocumentSkewDetectResult.getResultCode() != 0) {
                                    Log.e("VoucherCameraActivity", "The picture does not meet the requirements.");
                                } else {
                                    // Recognition success.
                                    Point leftTop = mlDocumentSkewDetectResult.getLeftTopPosition();
                                    Point rightTop = mlDocumentSkewDetectResult.getRightTopPosition();
                                    Point leftBottom = mlDocumentSkewDetectResult.getLeftBottomPosition();
                                    Point rightBottom = mlDocumentSkewDetectResult.getRightBottomPosition();

                                    Point[] points = {leftTop, rightTop, rightBottom, leftBottom};
                                    cropView.setCropPoints(points);
                                    cropImageView.setCropPoints(points);
                                    cropImageView.setImageBitmap(bitmap);
                                }
                            }
                        });
                    }
                });
            }
            return Unit.INSTANCE;
        });
        gpuImageView.setRotation(getRotation(cameraLoader.getCameraOrientation()));
        gpuImageView.setRenderMode(GPUImageView.RENDERMODE_CONTINUOUSLY);
        if (!loadCamera().hasMultipleCamera()) {
            gpuImageView.setVisibility(View.GONE);
        }
    }


    @Override
    protected void onResume() {
        super.onResume();
        doOnLayout(gpuImageView, new Function1<View, Void>() {
            @Override
            public Void invoke(View view) {
                loadCamera().onResume(view.getWidth(), view.getHeight());
                return null;
            }
        });
    }

    @Override
    protected void onPause() {
        loadCamera().onPause();
        super.onPause();
    }

    private void doOnLayout(View view, Function1<View, Void> function) {
        if (ViewCompat.isLaidOut(view) && !view.isLayoutRequested()) {
            function.invoke(view);
        } else {
            doOnNextLayout(view, function);
        }
    }

    private void doOnNextLayout(View view, Function1<View, Void> action) {
        view.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
            @Override
            public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
                view.removeOnLayoutChangeListener(this);
                action.invoke(view);
            }
        });
    }

    private Rotation getRotation(int orientation) {
        switch (orientation) {
            case 90:
                return Rotation.ROTATION_90;
            case 180:
                return Rotation.ROTATION_180;
            case 270:
                return Rotation.ROTATION_270;
            default:
                return Rotation.NORMAL;
        }
    }

    private volatile boolean isAutoDocument = false;
    private final ExecutorService executorService = Executors.newSingleThreadExecutor();
    private NV21ToBitmap nv21ToBitmap;
    private MLDocumentSkewCorrectionAnalyzer analyzer;

    private MLDocumentSkewCorrectionAnalyzer getMLDocAnalyzer() {
        if (analyzer == null) {
            MLDocumentSkewCorrectionAnalyzerSetting setting = new MLDocumentSkewCorrectionAnalyzerSetting.Factory().create();
            analyzer = MLDocumentSkewCorrectionAnalyzerFactory.getInstance()
                .getDocumentSkewCorrectionAnalyzer(setting);
        }
        return analyzer;
    }

    //auto document
    private CropView cropView;
    private CropImageView cropImageView;

    private void switchAutoDocument(View view) {
        Object tag = view.getTag();
        if (tag != null) {
            view.setTag(null);
            isAutoDocument = false;
            cropView.setVisibility(View.GONE);
            cropImageView.setVisibility(View.GONE);
        } else {
            view.setTag(1);
            isAutoDocument = true;
            cropView.setVisibility(View.VISIBLE);
            cropImageView.setVisibility(View.VISIBLE);
        }
    }
}
